package com.java.simple.study.jvm.gc.reference;

import com.java.simple.study.tool.Sleeper;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zhoujunlin
 * @date 2022年11月08日 11:28
 * @desc
 */
@Slf4j(topic = "c.PhantomReferenceTest")
public class PhantomReferenceTest {
    /**
     * 当前类对象
     */
    public static PhantomReferenceTest obj;
    /**
     * 引用队列
     */
    static ReferenceQueue<PhantomReferenceTest> phantomQueue;

    public static class CheckRefQueue extends Thread {
        @Override
        public void run() {
            while (true) {
                if (Objects.nonNull(phantomQueue)) {
                    PhantomReference<PhantomReferenceTest> objFromQueue = null;
                    try {
                        objFromQueue = (PhantomReference<PhantomReferenceTest>) phantomQueue.remove();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (Objects.nonNull(objFromQueue)) {
                        log.debug("追踪垃圾回收过程：PhantomReferenceTest实例被GC了");
                    }
                }
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        // finalize只会被调用一次
        super.finalize();
        log.debug("调用当前类的finalize方法");
        //重生一次
        obj = this;
    }

    public static void main(String[] args) {
        CheckRefQueue checkRefQueueThread = new CheckRefQueue();
        // 设为守护线程：当程序中没有非守护线程时 守护线程也就执行结束了
        checkRefQueueThread.setDaemon(true);
        checkRefQueueThread.setName("CheckRefQueueThread");
        checkRefQueueThread.start();

        phantomQueue = new ReferenceQueue<PhantomReferenceTest>();
        obj = new PhantomReferenceTest();
        // 构造了PhantomReferenceTest对象的虚引用并指定了引用队列
        PhantomReference<PhantomReferenceTest> phantomReference = new PhantomReference<>(obj, phantomQueue);
        try {
            // 不可获取虚引用中的对象
            log.debug(phantomReference.get() + "");
            log.debug("第一次GC");
            // 将强引用去除
            obj = null;
            // 第一次进行GC 由于对象可复活 GC无法回收该对象
            System.gc();
            Sleeper.sleep(1);
            if (Objects.isNull(obj)) {
                log.debug("obj是null");
            } else {
                log.debug("obj可用");
            }
            log.debug("第二次GC");
            obj = null;
            // 一旦将obj对象回收 就会将此虚引用存放到引用队列
            System.gc();
            Sleeper.sleep(1);
            if (Objects.isNull(obj)) {
                log.debug("obj是null");
            } else {
                log.debug("obj可用");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
